/**
 * Http请求
 */

/** 请求类型 */
export type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'HEAD' | 'OPTIONS' | 'TRACE';

/** 请求数据格式 */
export type DataType = 'json' | 'text';

/**  请求响应的数据类型 */
export type ResponseType = 'arraybuffer' | 'text';

/** 请求配置 */
export interface RequestConfig {
    // 请求地址
    url?: string;
    // 请求参数
    data?: object | string | ArrayBuffer;
    // 请求的 header
    header?: object;
    // 请求类型
    method?: Method;
    originalData?: boolean;
    // 超时时间，单位 ms
    timeout?: number;
    // 如果设为 json，会尝试对返回的数据做一次 JSON.parse
    dataType?: DataType;
    // 设置响应的数据类型。合法值：text、arraybuffer
    responseType?: ResponseType;
    // 验证 ssl 证书
    sslVerify?: boolean;
    // 跨域请求时是否携带凭证（cookies）
    withCredentials?: boolean;
    // DNS解析时优先使用ipv4
    firstIpv4?: boolean;
    // 收到开发者服务器成功返回的回调函数
    success?: Function;
    // 接口调用失败的回调函数
    fail?: Function;
    // 接口调用结束的回调函数（调用成功、失败都会执行）
    complete?: Function;
}

// 请求默认配置
let config: RequestConfig = {
    // 请求地址
    url: '',
    // 请求参数
    data: {},
    // 请求的 header,默认json请求
    header: {
        'content-type': 'application/json',
    },
    // 请求类型
    method: 'POST',
    originalData: true,
    // 超时时间，单位 ms
    timeout: 60000,
    // 如果设为 json，会尝试对返回的数据做一次 JSON.parse
    dataType: 'json',
    // 设置响应的数据类型。合法值：text、arraybuffer
    responseType: 'text',
    // 验证 ssl 证书
    sslVerify: true,
    // 跨域请求时是否携带凭证（cookies）
    withCredentials: false,
    // DNS解析时优先使用ipv4
    firstIpv4: false,
};

/** http数据请求 */
class Request {
    // 设置全局默认配置
    setConfig(customConfig: RequestConfig) {
        // 合并对象
        config = { ...config, ...(customConfig || {}) };
        console.log('设置全局默认配置');
        console.log(config);
    }

    // 拦截器
    interceptor: any = {
        // 请求前的拦截
        request: Function,
        // 请求后的拦截
        response: Function,
    };

    // 主要请求部分
    request<T>(cfg: RequestConfig = config): Promise<T> {
        let newConfig = { ...config, ...cfg };

        // 请求地址
        newConfig.url = config.url + '' + newConfig.url;
        const self = this;

        // 检查 -> 请求拦截器
        if (self.interceptor.request && typeof self.interceptor.request === 'function') {
            const opts = self.interceptor.request(newConfig);
            // 如果拦截器返回false，则取消promise
            if (opts === false) {
                return new Promise(() => {});
            }
            // 获取请求拦截器方法返回的配置，合并对象
            newConfig = { ...newConfig, ...opts };
        }

        return new Promise((resolve, reject) => {
            uni.request({
                url: newConfig.url || '',
                data: newConfig.data,
                header: newConfig.header,
                method: newConfig.method,
                timeout: newConfig.timeout,
                dataType: newConfig.dataType,
                responseType: newConfig.responseType,
                sslVerify: newConfig.sslVerify,
                withCredentials: newConfig.withCredentials,
                firstIpv4: newConfig.firstIpv4,
                complete(res: any) {
                    // 检查 -> 响应拦截器
                    if (self.interceptor.response && typeof self.interceptor.response === 'function') {
                        const opts = self.interceptor.response(res);
                        // 如果拦截器返回false,则将拦截器返回的内容给then回调
                        if (opts !== false) {
                            // 响应拦截器返回的内容
                            resolve(opts);
                        } else {
                            // 如果响应拦截器返回false，意味着拦截器定义者认为返回有问题，直接接入catch回调
                            reject(res);
                        }
                    } else {
                        // 返回最原始的数据
                        resolve(res);
                    }
                },
            });
        });
    }

    // get请求
    get<T = any>(url: string, data: object = {}, opts?: RequestConfig): Promise<T> {
        const cfg: RequestConfig = { ...config, ...(opts || {}), url: url, method: 'GET', data: data };
        return this.request(cfg);
    }

    // post请求
    post<T = any>(url: string, data: object = {}, opts?: RequestConfig): Promise<T> {
        const cfg: RequestConfig = { ...config, ...(opts || {}), url: url, method: 'POST', data: data };
        return this.request(cfg);
    }
}

export default new Request();
